﻿/*
 * FaceCat图形通讯框架
 * 著作权编号:2015SR229355+2020SR0266727
 * 上海卷卷猫信息技术有限公司
 */

using System;
using System.Collections.Generic;
using System.Net;
using System.IO;
using System.Text;
using System.Runtime.InteropServices;
using System.Threading;
using System.IO.Compression;

namespace FaceCat {
    /// <summary>
    /// 监听消息
    /// </summary>
    public interface FCListenerMessageCallBack
    {
        /// <summary>
        /// 回调方法
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="message"></param>
        /// <param name="invoke"></param>
        void callListenerMessageEvent(Object sender, FCMessage message, Object invoke);
    }

    /// <summary>
    /// 消息结构
    /// </summary>
    public class FCMessage
    {
        /// <summary>
        /// 创建消息
        /// </summary>
        public FCMessage()
        {
        }

        /// <summary>
        /// 创建消息
        /// </summary>
        /// <param name="serviceID">服务ID</param>
        /// <param name="functionID">功能ID</param>
        /// <param name="requestID">请求ID</param>
        /// <param name="socketID">连接ID</param>
        /// <param name="state">状态</param>
        /// <param name="compressType">压缩类型</param>
        /// <param name="bodyLength">包体长度</param>
        /// <param name="body">包体</param>
        public FCMessage(int serviceID, int functionID, int requestID, int socketID, int state, int compressType, int bodyLength, byte[] body)
        {
            m_serviceID = serviceID;
            m_functionID = functionID;
            m_requestID = requestID;
            m_socketID = socketID;
            m_state = state;
            m_compressType = compressType;
            m_bodyLength = bodyLength;
            m_body = body;
        }

        /// <summary>
        /// 服务ID
        /// </summary>
        public int m_serviceID;

        /// <summary>
        /// 功能ID
        /// </summary>
        public int m_functionID;

        /// <summary>
        /// 请求ID
        /// </summary>
        public int m_requestID;

        /// <summary>
        /// 连接ID
        /// </summary>
        public int m_socketID;

        /// <summary>
        /// 状态
        /// </summary>
        public int m_state;

        /// <summary>
        /// 压缩类型
        /// </summary>
        public int m_compressType;

        /// <summary>
        /// 包体长度
        /// </summary>
        public int m_bodyLength;

        /// <summary>
        /// 包体
        /// </summary>
        public byte[] m_body;

        /// <summary>
        /// 信号
        /// </summary>
        public int m_signal;

        /// <summary>
        /// 复制数据
        /// </summary>
        /// <param name="message">消息</param>
        public void copy(FCMessage message)
        {
            m_serviceID = message.m_serviceID;
            m_functionID = message.m_functionID;
            m_requestID = message.m_requestID;
            m_socketID = message.m_socketID;
            m_state = message.m_state;
            m_compressType = message.m_compressType;
            m_bodyLength = message.m_bodyLength;
            m_body = message.m_body;
        }
    }

    /// <summary>
    /// 消息监听
    /// </summary>
    public class FCMessageListener
    {
        /// <summary>
        /// 创建消息监听
        /// </summary>
        public FCMessageListener()
        {
        }

        /// <summary>
        /// 析构方法
        /// </summary>
        ~FCMessageListener()
        {
            clear();
        }

        /// <summary>
        /// 监听回调列表
        /// </summary>
        private ArrayList<FCListenerMessageCallBack> m_callBacks = new ArrayList<FCListenerMessageCallBack>();

        /// <summary>
        /// 监听回调列表
        /// </summary>
        private ArrayList<Object> m_invokes = new ArrayList<Object>();

        /// <summary>
        /// 添加回调
        /// </summary>
        /// <param name="callBack">回调</param>
        public void add(FCListenerMessageCallBack callBack, Object invoke)
        {
            m_callBacks.Add(callBack);
            m_invokes.add(invoke);
        }

        /// <summary>
        /// 回调方法
        /// </summary>
        public void callBack(FCMessage message)
        {
            int callBackSize = m_callBacks.size();
            for (int i = 0; i < callBackSize; i++)
            {
                m_callBacks.get(i).callListenerMessageEvent(m_callBacks.get(i), message, m_invokes.get(i));
            }
        }

        /// <summary>
        /// 清除监听
        /// </summary>
        public void clear()
        {
            m_callBacks.Clear();
            m_invokes.clear();
        }

        /// <summary>
        /// 移除回调
        /// </summary>
        /// <param name="callBack">回调</param>
        public void remove(FCListenerMessageCallBack callBack)
        {
            int callBacksSize = m_callBacks.size();
            for (int i = 0; i < callBacksSize; i++)
            {
                if (m_callBacks.get(i) == callBack)
                {
                    m_callBacks.removeAt(i);
                    m_invokes.removeAt(i);
                    i--;
                    callBacksSize--;
                }
            }
        }
    }

    /// <summary>
    /// 基础服务类
    /// </summary>
    public class FCClientService {
        /// <summary>
        /// 创建服务
        /// </summary>
        public FCClientService() {
        }

        /// <summary>
        /// 析构函数
        /// </summary>
        ~FCClientService() {
            lock (m_listeners) {
                m_listeners.Clear();
            }
        }

        /// <summary>
        /// 无压缩
        /// </summary>
        public static int COMPRESSTYPE_NONE = 0;

        /// <summary>
        /// GZIP压缩
        /// </summary>
        public static int COMPRESSTYPE_GZIP = 1;

        /// <summary>
        /// 监听者集合
        /// </summary>
        private HashMap<int, FCMessageListener> m_listeners = new HashMap<int, FCMessageListener>();

        /// <summary>
        /// 请求ID
        /// </summary>
        private static int m_requestID = 10000;

        /// <summary>
        /// 所有的服务
        /// </summary>
        public static ArrayList<FCClientService> m_services = new ArrayList<FCClientService>();


        private int m_compressType = COMPRESSTYPE_NONE;

        /// <summary>
        /// 获取压缩类型
        /// </summary>
        public virtual int getCompressType() {
            return m_compressType;
        }

        /// <summary>
        /// 设置压缩类型
        /// </summary>
        public virtual void setCompressType(int value) {
            m_compressType = value;
        }

        public static long m_downFlow;

        /// <summary>
        /// 获取下载流量
        /// </summary>
        public static long getDownFlow() {
            return FCClientService.m_downFlow;
        }

        /// <summary>
        /// 设置下载流量
        /// </summary>
        public static void setDownFlow(long value) {
            FCClientService.m_downFlow = value;
        }

        private bool m_isDeleted = false;

        /// <summary>
        /// 获取对象是否已被销毁
        /// </summary>
        public virtual bool isDeleted() {
            return m_isDeleted;
        }

        private int m_serviceID = 0;

        /// <summary>
        /// 获取服务的ID
        /// </summary>
        public virtual int getServiceID() {
            return m_serviceID;
        }

        /// <summary>
        /// 设置服务的ID
        /// </summary>
        public virtual void setServiceID(int value) {
            m_serviceID = value;
        }

        public static long m_upFlow;

        /// <summary>
        /// 获取上传流量
        /// </summary>
        public static long getUpFlow() {
            return m_upFlow;
        }

        /// <summary>
        /// 设置上传流量
        /// </summary>
        public static void setUpFlow(long value) {
            m_upFlow = value;
        }

        /// <summary>
        /// 连接到服务器
        /// </summary>
        /// <param name="proxyType">代理类型</param>
        /// <param name="ip">IP</param>
        /// <param name="port">端口</param>
        /// <param name="proxyIp">代理IP</param>
        /// <param name="proxyPort">代理端口</param>
        /// <param name="proxyUserName">用户名</param>
        /// <param name="proxyUserPwd">密码</param>
        /// <param name="proxyDomain">域</param>
        /// <param name="timeout">超时</param>
        /// <returns>状态</returns>
        public static int connectToServer(int proxyType, String ip, int port, String proxyIp, int proxyPort, String proxyUserName, String proxyUserPwd, String proxyDomain, int timeout, byte[] token) {
            FCClientSockets.setListener(new FCClientSocketListener());
            return FCClientSockets.connect(proxyType, ip, (short)port, proxyIp, (short)proxyPort, proxyUserName, proxyUserPwd, proxyDomain, timeout, token);
        }

        /// <summary>
        /// 发送消息
        /// </summary>
        /// <param name="socketID">套接字ID</param>
        /// <param name="str">数据</param>
        /// <param name="len">长度</param>
        /// <returns>状态</returns>
        public static int sendByClient(int socketID, byte[] str, int len) {
            return FCClientSockets.send(socketID, str, len);
        }

        /// <summary>
        /// 添加服务
        /// </summary>
        /// <param name="service">服务</param>
        public static void addService(FCClientService service) {
            m_services.add(service);
        }

        /// <summary>
        /// 连接服务器
        /// </summary>
        /// <param name="ip">地址</param>
        /// <param name="port">端口</param>
        public static int connect(String ip, int port, byte[] token) {
            int socketID = connectToServer(0, ip, port, "", 0, "", "", "", 6, token);
            return socketID;
        }

        /// <summary>
        /// 回调函数
        /// </summary>
        /// <param name="socketID">连接ID</param>
        /// <param name="localSID">本地连接ID</param>
        /// <param name="str">数据</param>
        /// <param name="len">长度</param>
        public static void callBack(int socketID, int localSID, byte[] bytes, int len) {
            m_downFlow += len;
            try {
                if (len > 4) {
                    FCBinary br = new FCBinary();
                    br.write(bytes, len);
                    int head = br.readInt();
                    int serviceID = br.readShort();
                    int matchCount = 0;
                    for (int i = 0; i < m_services.size(); i++)
                    {
                        FCClientService clientService = m_services.get(i);
                        if (clientService.getServiceID() == serviceID)
                        {
                            if (matchCount > 0)
                            {
                                FCBinary brNew = new FCBinary();
                                brNew.write(bytes, len);
                                brNew.readInt();
                                brNew.readShort();
                                clientService.onCallBack(brNew, socketID, localSID, len);
                                brNew.close();
                            }
                            else
                            {
                                clientService.onCallBack(br, socketID, localSID, len);
                            }
                            matchCount++;
                        }
                    }
                    br.close();
                }
            } catch (Exception ex) {
                Console.WriteLine(ex.Message + "\r\n" + ex.StackTrace);
            }
        }

        /// <summary>
        /// 销毁对象
        /// </summary>
        public virtual void delete() {
            if (!m_isDeleted) {
                m_listeners.Clear();
                m_isDeleted = true;
            }
        }

        /// <summary>
        /// 获取请求ID
        /// </summary>
        /// <returns>请求ID</returns>
        public static int getRequestID() {
            return m_requestID++;
        }

        /// <summary>
        /// 保持活跃
        /// </summary>
        /// <param name="socketID">套接字ID</param>
        /// <returns>状态</returns>
        public virtual int keepAlive(int socketID) {
            FCBinary bw = new FCBinary();
            bw.writeInt((int)4);
            byte[] bytes = bw.getBytes();
            int length = bytes.Length;
            int ret = FCClientSockets.send(socketID, bytes, length);
            bw.close();
            return ret;
        }

        /// <summary>
        /// 收到消息
        /// </summary>
        /// <param name="br">流</param>
        /// <param name="socketID">套接字ID</param>
        /// <param name="localSID">本地套接字ID</param>
        /// <param name="len">长度</param>
        public virtual void onCallBack(FCBinary br, int socketID, int localSID, int len) {
            int headSize = sizeof(int) * 3 + sizeof(short) * 2 + sizeof(byte) * 2;
            int functionID = br.readShort();
            int requestID = br.readInt();
            int state = br.readByte();
            int compressType = br.readByte();
            int bodyLength = br.readInt();
            byte[] body = new byte[len - headSize];
            br.readBytes(body);
            if (compressType == COMPRESSTYPE_GZIP) {
                using (MemoryStream dms = new MemoryStream()) {
                    using (MemoryStream cms = new MemoryStream(body)) {
                        using (GZipStream gzip = new GZipStream(cms, CompressionMode.Decompress)) {
                            byte[] buffer = new byte[1024];
                            int size = 0;
                            while ((size = gzip.Read(buffer, 0, buffer.Length)) > 0) {
                                dms.Write(buffer, 0, size);
                            }
                            int oLen = body.Length;
                            body = dms.ToArray();
                        }
                    }
                }
            }
            FCMessage message = new FCMessage(getServiceID(), functionID, requestID, socketID, state, compressType, bodyLength, body);
            onReceive(message);
            body = null;
        }

        /// <summary>
        /// 客户端关闭方法
        /// </summary>
        /// <param name="socketID">连接ID</param>
        /// <param name="localSID">本地连接ID</param>
        public virtual void onClientClose(int socketID, int localSID) {
        }

        /// <summary>
        /// 客户端连接方法
        /// </summary>
        /// <param name="socketID">连接ID</param>
        /// <param name="localSID">本地连接ID</param>
        public virtual void onClientConnect(int socketID, int localSID) {
        }

        /// <summary>
        /// 接收数据
        /// </summary>
        /// <param name="message">消息</param>
        public virtual void onReceive(FCMessage message) {
        }

        /// <summary>
        /// 注册数据监听
        /// </summary>
        /// <param name="requestID">请求ID</param>
        /// <param name="callBack">回调函数</param>
        public virtual void addListener(int requestID, FCListenerMessageCallBack callBack, Object invoke) {
            lock (m_listeners) {
                FCMessageListener listener = null;
                if (!m_listeners.containsKey(requestID)) {
                    listener = new FCMessageListener();
                    m_listeners.put(requestID, listener);
                } else {
                    listener = m_listeners.get(requestID);
                }
                listener.add(callBack, invoke);
            }
        }

        /// <summary>
        /// 发送消息
        /// </summary>
        /// <param name="message">消息</param>
        public virtual int send(FCMessage message) {
            FCBinary bw = new FCBinary();
            byte[] body = message.m_body;
            int bodyLength = message.m_bodyLength;
            int uncBodyLength = bodyLength;
            if (uncBodyLength >= 200)
            {
                if (message.m_compressType == COMPRESSTYPE_GZIP)
                {
                    using (MemoryStream cms = new MemoryStream())
                    {
                        using (GZipStream gzip = new GZipStream(cms, CompressionMode.Compress))
                        {
                            gzip.Write(body, 0, body.Length);
                        }
                        body = cms.ToArray();
                        bodyLength = body.Length;
                    }
                }
            }
            else
            {
                message.m_compressType = COMPRESSTYPE_NONE;
            }
            int len = sizeof(int) * 3 + bodyLength + sizeof(short) * 2 + sizeof(byte) * 2;
            bw.writeInt(len);
            bw.writeShort((short)message.m_serviceID);
            bw.writeShort((short)message.m_functionID);
            bw.writeInt(message.m_requestID);
            bw.writeByte((byte)message.m_state);
            bw.writeByte((byte)message.m_compressType);
            bw.writeInt(uncBodyLength);
            bw.writeBytes(body);
            byte[] bytes = bw.getBytes();
            int length = bytes.Length;
            int ret = FCClientSockets.send(message.m_socketID, bytes, length);
            m_upFlow += ret;
            bw.close();
            return ret;
        }

        /// <summary>
        /// 发送到监听者
        /// </summary>
        /// <param name="message">消息</param>
        public virtual void sendToListener(FCMessage message) {
            FCMessageListener listener = null;
            lock (m_listeners) {
                if (m_listeners.containsKey(message.m_requestID)) {
                    listener = m_listeners.get(message.m_requestID);
                }
            }
            if (listener != null) {
                listener.callBack(message);
            }
        }

        /// <summary>
        /// 取消注册数据监听
        /// </summary>
        /// <param name="requestID">请求ID</param>
        public virtual void removeListener(int requestID) {
            lock (m_listeners) {
                m_listeners.Remove(requestID);
            }
        }

        /// <summary>
        /// 写日志
        /// </summary>
        /// <param name="socketID">连接ID</param>
        /// <param name="localSID">本地连接ID</param>
        /// <param name="state">状态</param>
        /// <param name="log">日志</param>
        public static void writeClientLog(int socketID, int localSID, int state, String log) {
            if (state == 2 || state == 3) {
                foreach (FCClientService service in m_services) {
                    service.onClientClose(socketID, localSID);
                }
            } else if (state == 1) {
                foreach (FCClientService service in m_services) {
                    service.onClientConnect(socketID, localSID);
                }
            }
        }
    }

    /// <summary>
    /// 客户端数据监听
    /// </summary>
    public class FCClientSocketListener : FCSocketListener {
        /// <summary>
        /// 构造函数
        /// </summary>
        public FCClientSocketListener() {
        }

        /// <summary>
        /// 数据回调
        /// </summary>
        /// <param name="socketID"></param>
        /// <param name="localSID"></param>
        /// <param name="str"></param>
        /// <param name="len"></param>
        public void callBack(int socketID, int localSID, byte[] str, int len) {
            FCClientService.callBack(socketID, localSID, str, len);
        }

        /// <summary>
        /// 写日志
        /// </summary>
        /// <param name="socketID"></param>
        /// <param name="localSID"></param>
        /// <param name="state"></param>
        /// <param name="log"></param>
        public void writeLog(int socketID, int localSID, int state, String log) {
            FCClientService.writeClientLog(socketID, localSID, state, log);
        }
    }
}